define(async () => {
    const util = await require("../util");

    /**
     * 图层类
     * 
     * 所有的实际绘制操作都将在此进行，先绘制于离屏canvas后再绘制于可见canvas
     * 实例化时不会创建DOM元素，仅在被添加到舞台时才会创建DOM元素
     */
    class Layer {
        
        id;  //图层ID
        width;  //图层宽度
        height;  //图层高度
        canvas;  //画布DOM
        canvasContext;  //画布上下文
        offscreenCanvas;  //离屏画布DOM
        offscreenContext;  //离屏画布上下文
        dirtyRects = [];  //脏矩形数组
        initialized = false;  //是否已完成初始化

        constructor(id, options = {}) {
            util.assert(util.isUndefined(id) || util.isString(id), new TypeError("layer id must be an String"));
            util.assert(util.isObject(options), "options must be an Object");
            const { width, height } = options;
            util.assert(util.isUndefined(width) || util.isNumber(width), new TypeError("width must be an Number"));
            util.assert(util.isUndefined(height) || util.isNumber(height), new TypeError("height must be an Number"));
            this.id = util.defaultTo(id, util.uniqueId("layer_"));  //提供ID或生成唯一ID
            this.width = util.defaultTo(width, 0);  //如指定宽度则使用否则等待init宽度
            this.height = util.defaultTo(height, 0);  //如指定高度则使用否则等待init高度
        }

        /**
         * 初始化图层
         * 
         * 对图层的操作必须在初始化之后
         * 此函数对图层宽高进行重设以及创建画布DOM和设置相关参数
         * 
         * @param {Number} width 图层宽度
         * @param {Number} height 图层高度
         */
        init(width, height) {
            this.width = this.width || width;
            this.height = this.height || height;
            this.canvas = document.createElement("canvas");   //创建可见画布
            this.canvas.className = "layer";
            this.canvas.setAttribute("id", this.id);
            this.canvas.width = this.width;
            this.canvas.height = this.height;
            this.canvasContext = this.canvas.getContext("2d");  //获得可见画布上下文
            // this.canvasContext.imageSmoothingEnabled = false;  关闭抗锯齿，未测试
            this.offscreenCanvas = document.createElement("canvas");  //创建离屏画布
            this.offscreenCanvas.width = this.width;
            this.offscreenCanvas.height = this.height;
            this.offscreenContext = this.offscreenCanvas.getContext("2d");  //获得离屏画布上下文
            // this.offscreenContext.imageSmoothingEnabled = false;  关闭抗锯齿，未测试
            this.initialized = true;  //设置初始化状态
        }

        /**
         * 渲染图层
         * 
         * 此函数负责将离屏画布绘制到可见画布上
         */
        render() {
            util.assert(this.initialized, `layer ${this.id} is not initialized`);
            this.canvasContext.drawImage(this.offscreenCanvas, 0, 0);  //将离屏画布的内容绘制到可见画布中
        }

        /**
         * 清除图层
         * 
         * 此函数负责清除离屏画布和可见画布上的脏矩形，非全部清除
         */
        clear() {
            util.assert(this.initialized, `layer ${this.id} is not initialized`);
            let dirtyRect;
            while((dirtyRect = this.dirtyRects.pop())) {  //获取所有脏矩形
                const [x, y, width, height] = dirtyRect;
                const maxLength = Math.max(width, height);  //获得宽或高最大值
                const diagonalLength = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2));  //计算脏矩形对角线长度
                const offsetX = (width > height ? 0 : (height - width) / 2) + 5;  //计算X轴偏移量
                const offsetY = (height > width ? 0 : (width - height) / 2) + 5;  //计算Y轴偏移量
                const clearX = x + maxLength / 2 - diagonalLength / 2 - offsetX;  //计算清除X轴起始坐标
                const clearY = y + maxLength / 2 - diagonalLength / 2 - offsetY;  //计算清除Y轴起始坐标
                this.canvasContext.clearRect(clearX, clearY, diagonalLength + 10, diagonalLength + 10);  //清除可见画布的脏矩形
                this.offscreenContext.clearRect(clearX, clearY, diagonalLength + 10, diagonalLength + 10);  //清除离屏画布的脏矩形
            }
        }

        /**
         * 添加脏矩形
         * 
         * 此函数用于将脏矩形添加队列，等待clear函数在下一帧之前清除它们
         * 
         * @param {Number} x 脏矩形X轴坐标
         * @param {Number} y 脏矩形Y轴坐标
         * @param {Number} width 脏矩形宽度
         * @param {Number} height 脏矩形高度
         */
        addDirtyRect(x, y, width, height) {
            util.assert(this.initialized, `layer ${this.id} is not initialized`);
            for(let dirtyRect of this.dirtyRects) {
                const offsetX = x - dirtyRect.x;
                const offsetY = y - dirtyRect.y;
                //当存在某个脏矩形大于或等于当前准备添加的脏矩形时不添加到清除列表
                if(offsetX >= 0 && offsetY >= 0 && width + offsetX <= dirtyRect.width && height + offsetY <= dirtyRect.height)
                    return;
            }
            this.dirtyRects.unshift([x, y, width, height]);
        }
        
        /**
         * 判断是否Layer实例
         * 
         * @param {Any} value 
         * @returns 
         */
        static isInstance(value) {
            return value instanceof Layer;
        }

    }

    return Layer;
});